package com.yugao.fintech.antelope.base.tenant.core;

import com.yugao.fintech.antelope.base.model.constants.ModuleCons;
import com.yugao.fintech.framework.assistant.utils.SqlUtils;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

@Getter
@Component
@Slf4j
public class TenantConfig {
    private List<String> associatedUserTables = new CopyOnWriteArrayList<>();
    private List<String> hasTenantIdTables = new CopyOnWriteArrayList<>();
    private List<String> hasMainTenantIdTables = new CopyOnWriteArrayList<>();


    @PostConstruct
    public void init() {
        hasTenantIdTables.clear();
        associatedUserTables.clear();
        Reflections reflection = new Reflections(new ConfigurationBuilder()
                .setScanners(Scanners.TypesAnnotated)
                .setUrls(ClasspathHelper.forPackage(ModuleCons.SCAN_BASE_PACKAGE))
                .filterInputsBy(new FilterBuilder()));
        Set<Class<?>> typesAnnotated = reflection.getTypesAnnotatedWith(DataIsolation.class);
        for (Class<?> aClass : typesAnnotated) {
            DataIsolation dataIsolation = aClass.getAnnotation(DataIsolation.class);
            process(dataIsolation);
        }

        hasTenantIdTables = hasTenantIdTables.stream().map(e -> e.toLowerCase(Locale.ROOT)).distinct().collect(Collectors.toList());
        associatedUserTables = associatedUserTables.stream().map(e -> e.toLowerCase(Locale.ROOT)).distinct().collect(Collectors.toList());
        hasMainTenantIdTables = hasMainTenantIdTables.stream().map(e -> e.toLowerCase(Locale.ROOT)).distinct().collect(Collectors.toList());
    }

    public void process(DataIsolation dataIsolation) {
        if (SqlUtils.containsSqlInjection(dataIsolation.tableName())) {
            throw new BizException("table name [{}] 存在sql注入", dataIsolation.tableName());
        }

        if (dataIsolation.filterTenant()) {
            log.info("tableName: {}, filter by tenant", dataIsolation.tableName());
            hasTenantIdTables.add(dataIsolation.tableName());
        }

        if (dataIsolation.associatedUser()) {
            log.info("tableName: {}, associated user", dataIsolation.tableName());
            associatedUserTables.add(dataIsolation.tableName());
        }

        if (dataIsolation.filterMainTenant()) {
            log.info("tableName: {}, filter by main tenant", dataIsolation.tableName());
            hasMainTenantIdTables.add(dataIsolation.tableName());
        }
    }
}
